package model.impl;

import java.awt.Color;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import main.ResOTCModel;
import model.impl.network.ElementImpl;
import model.impl.network.LightImpl;

import org.jdom2.JDOMException;
import org.resotc.model.Element;
import org.resotc.model.ElementType;
import org.resotc.model.Individual;
import org.resotc.model.Intersection;
import org.resotc.model.Lane;
import org.resotc.model.Light;
import org.resotc.model.Phasen;
import org.resotc.model.Queue;
import org.resotc.model.Track;

import sim.engine.SimState;
import sim.engine.Steppable;
import sim.portrayal.continuous.ContinuousPortrayal2D;
import sim.util.Double2D;

public class IntersectionImpl3 extends ElementImpl implements Intersection,
		Steppable {

	private boolean isQueueLeft;
	private HashMap<Integer, Queue> queues;
	private double currentTime;
	private ResOTCModel simulation;
	private HashMap<Phasen, Integer> phasensystem;
	private Phasen currentPhase;
	private long phasenCounter;
	ContinuousPortrayal2D trafficLightPortrayal = new ContinuousPortrayal2D();

	public IntersectionImpl3(int id, boolean isQueueLeft, double xPos,
			double yPos) throws JDOMException, IOException {
		super(id, ElementType.INTERSECTION, xPos, yPos);
		this.queues = new HashMap<Integer, Queue>();
		this.isQueueLeft = isQueueLeft;
		currentPhase = Phasen.FIRST;
	}

	/**
	 * @see Intersection#getPhaseSystem()
	 */
	public HashMap<Phasen, Integer> getPhaseSystem() {
		return this.phasensystem;
	}

	/**
	 * @see Intersection#isQueueLeft()
	 */
	public boolean isQueueLeft() {
		return isQueueLeft;
	}

	/**
	 * @see Intersection#addArrivingIndividual(Individual, int)
	 */
	public void addArrivingIndividual(Individual ind, int trackId) {
		List<Queue> queueList = getQueues();

		for (Queue queue : queueList) {
			if (queue.getTracks().containsKey(trackId)) {
				queue.addIndividualToTrack(trackId, ind);
			}
		}
	}

	/**
	 * @see Intersection#getNumberOfIndividuals()
	 */
	public int getNumberOfIndividuals() {
		int numberOfIndividuals = 0;
		List<Queue> queueList = getQueues();

		for (Queue queue : queueList) {
			List<Track> trackList = new ArrayList<Track>(queue.getTracks()
					.values());

			for (Track track : trackList) {
				numberOfIndividuals += track.getNumberOfIndividuals();
			}
		}
		return numberOfIndividuals;
	}

	/**
	 * @see Intersection#determineNeighbours()
	 * @param laneId
	 * @return
	 */
	public HashMap<Integer, Element> determineAccessibleNeighbours(int laneId) {
		HashMap<Integer, Element> accNeighbours = new HashMap<Integer, Element>();
		List<Queue> queueList = getQueues();

		for (Queue queue : queueList) {
			List<Track> trackList = new ArrayList<Track>(queue.getTracks()
					.values());

			for (Track track : trackList) {
				if (!track.getDstTracks().isEmpty()) {
					List<Lane> laneList = new ArrayList<Lane>(queue.getStreet()
							.getLanes().values());
					for (Lane lane : laneList) {
						if (!(lane.getSuccessor() == this)) {
							accNeighbours.put(lane.getSuccessor().getId(),
									lane.getSuccessor());
						}
					}
				}
			}
		}
		return accNeighbours;
	}

	/**
	 * @see Intersection#initializeQueues(HashMap)
	 */
	public void initializeQueues(HashMap<Integer, Queue> queues) {
		List<Queue> queueList = new ArrayList<Queue>(queues.values());
		for (Queue queue : queueList) {
			if (queue.getComponentType().equals(this.getElementType())
					&& queue.getComponentTypeId() == this.getId()) {
				this.queues.put(queue.getId(), queue);
			}
		}
	}

	/**
	 * @see Intersection#getQueues()
	 */
	public ArrayList<Queue> getQueues() {
		return new ArrayList<Queue>(queues.values());
	}

	public void step(SimState state) {
		simulation = (ResOTCModel) state;
		this.queues = simulation.getQueueDAO().getQueues();
		currentTime = simulation.schedule.getTime();
		trafficLightPortrayal.setField(simulation.trafficLightContinuous2D);
		for (Queue queue : simulation.getTrafficQueueList()) {

			HashMap<Integer, Track> tracks = queue.getTracks();
			for (Track track : tracks.values()) {
				HashMap<Integer, Double2D> lights = queue.getTrafficLight()
						.getLightPos();

				for (Double2D lightPos : lights.values()) {
					if (track.getElementType() == ElementType.INCOMING) {
						Light tl = queue.getTrafficLight().getLight(
								queue.getTrackLightId(track.getId()));

						trafficLightPortrayal.setPortrayalForObject(lightPos,
								tl);
						simulation.trafficLightContinuous2D.setObjectLocation(
								tl, lightPos);
					}
				}
			}
		}

		for (Queue queue : queues.values()) {
			if (queue.getComponentType() == this.getElementType()
					&& queue.getComponentTypeId() == this.getId()) {
				// this.calcPhasenSystem(currentTime, queue);
				this.calcPhaseSystem(queue);
			}
		}
		this.changePhasenSystem();
	}

	public void changePhasenSystem() {
		this.phasenCounter++;
		if (this.phasensystem.containsKey(currentPhase)) {
			if (this.phasenCounter > phasensystem.get(currentPhase)) {
				this.currentPhase = currentPhase.getNext();
				this.phasenCounter = 0;
			}
		} else {
			this.currentPhase = Phasen.FIRST;
			this.phasenCounter = 0;
		}
	}

	public void calcPhaseSystem(double time, Queue queue) {
		HashMap<Integer, Track> tracks = queue.getTracks();

		for (Track track : tracks.values()) {
			if (track.getPhase() == currentPhase) {

			}
		}
	}

	public void moveIndividual(Queue queue) {
		HashMap<Integer, Track> tracks = queue.getTracks();

		for (Track track : tracks.values()) {
			if (track.getPhase() == currentPhase
					&& track.getElementType() == ElementType.INCOMING) {
				LinkedList<Individual> individuals = track.getTrack();
				List<Track> destTracks = track.getDstTracks();
				int nextInt = new Random().nextInt(destTracks.size());
				Track dstTrack = destTracks.get(nextInt);

				if (!dstTrack.isTrackOverloaded()) {
					Queue dstQueue = queues.get(dstTrack.getQueueId());

					if (individuals.size() > 0) {
						Individual individual = individuals.getLast();

						queue.deleteIndividualFromTrack(track.getId(),
								individual);
						//queue.deleteIndividualFromLane(individual.getLaneId(),
						//		individual);
						dstQueue.addIndividualToTrack(dstTrack.getId(),
								individual);
						
						ArrayList<Lane> depLanes = dstQueue.getStreet().getDepartureLanes(this);
						int nextLane = new Random().nextInt(depLanes.size());
						dstQueue.addIndividualToLane(depLanes.get(nextLane).getId(),
								individual);
					}
				}
			}
		}
	}

	public void setTrafficLightColor(Queue queue) {
		HashMap<Integer, Track> tracks = queue.getTracks();

		for (Track track : tracks.values()) {
			if (track.getElementType() == ElementType.INCOMING) {
				int trackLightId = queue.getTrackLightId(track.getId());
				if (track.getPhase() == currentPhase) {
					queue.getTrafficLight().setColor(trackLightId,
							new LightImpl(Color.green));
				} else {
					queue.getTrafficLight().setColor(trackLightId,
							new LightImpl(Color.red));
				}
			}
		}
	}

	public void calcPhaseSystem(Queue queue) {
		moveIndividual(queue);
		setTrafficLightColor(queue);
	}

	public void calcPhasenSystem(double time, Queue queue) {

		HashMap<Integer, Track> tracks = queue.getTracks();
		for (Track track : tracks.values()) {

			if (track.getPhase() == currentPhase) {
				if (track.getElementType() == ElementType.INCOMING) {
					queue.getTrafficLight().setColor(
							queue.getTrackLightId(track.getId()),
							new LightImpl(Color.green));
				}
				List<Individual> IndividualList = track.getTrack();
				for (Individual individual : IndividualList) {
					List<Track> destTracks = track.getDstTracks();
					Random random = new Random();
					Track destTrack = destTracks.get(random.nextInt(destTracks
							.size()));
					if (!destTrack.isTrackOverloaded()) {
						Queue destQueue = queues.get(destTrack.getQueueId());
						destQueue.addIndividualToTrack(destTrack.getId(),
								individual);
						queue.deleteIndividualFromTrack(track.getId(),
								individual);

						HashMap<Integer, Lane> lanes = destQueue.getStreet()
								.getLanes();

						for (Lane lane : lanes.values()) {
							HashMap<Integer, Track> srcTracksLanes = lane
									.getSrcTracks();

							for (Track srcTrack : srcTracksLanes.values()) {

								if (srcTrack.getId() == destTrack.getId()) {
									destQueue.addIndividualToLane(lane.getId(),
											individual);
									individual.setNumberOfStops(individual
											.getNumberOfStops() + 1);
									break;
								}
							}
							break;
						}
						break;
					}
				}
				break;
			} else {
				if (track.getElementType() == ElementType.INCOMING) {
					queue.getTrafficLight().setColor(
							queue.getTrackLightId(track.getId()),
							new LightImpl(Color.red));
				}
			}
		}
	}

	public void setPhaseSystem(HashMap<Phasen, Integer> phasensystem) {
		this.phasensystem = phasensystem;
	}

	public Phasen getCurrentPhase() {
		return currentPhase;
	}
}